import { WsClient } from "tsrpc-browser";
import { tgxSceneUtil, tgxUIAlert, } from "../../core_tgx/tgx";
import { SceneDef, SubWorldSceneParams } from "../../scripts/SceneDef";
import { ServiceType } from "../shared/protocols/serviceProto_worldServer";
import { NetUtil } from "./NetUtil";
import { UserMgr } from "./UserMgr";
import { WorldMgr } from "./WorldMgr";
import { director } from "cc";
import { MsgUserComesToTablePush } from "../shared/protocols/worldServer/MsgUserComesToTablePush";
import { MsgUserDataChangedPush } from "../shared/protocols/worldServer/MsgUserDataChangedPush";
import { MsgUserLeavesFromTablePush } from "../shared/protocols/worldServer/MsgUserLeavesFromTablePush";
import { IUserData, SubWorldData } from "../shared/types/SubWorldData";
import { MsgSubWorldDataChangedPush } from "../shared/protocols/worldServer/MsgSubWorldDataChangedPush";
import { MsgSubWorldDataSyncPush } from "../shared/protocols/worldServer/MsgSubWorldDataSyncPush";
import { SubGameMgr } from "./SubGameMgr";
import { MsgSubWorldDismissedPush } from "../shared/protocols/worldServer/MsgSubWorldDismissedPush";

export class GameMode {
    public static SOLO: string = 'solo';
    public static AI: string = 'ai';
    public static ONLINE: string = 'online';
}

export class SubWorldEvent {
    /**
     * @en PN is used for notifying data changed, then the client can get the new data from the master server.
     * @zh 用于通知客户端哪些数据变动，方便从 Master 获取最新数据
     * 
    */
    public static PN:string = 'SubWorldEvent.PN';

    public static NEW_USER_COMES: string = 'SubWorldEvent.NEW_USER_IN_TABLE';
    public static WATCHER_LIST_CHANGED: string = 'SubWorldEvent.WATCHER_LIST_CHANGED';
    public static USER_LEAVES: string = 'SubWorldEvent.USER_LEAVES';
    public static USER_DATA_CHANGED: string = 'SubWorldEvent.USER_DATA_CHANGED';
}

export class GameMgr {

    protected _data: SubWorldData
    public get data(): SubWorldData {
        return this._data;
    }

    protected _gameMode: string = GameMode.SOLO;
    public get gameMode(): string {
        return this._gameMode;
    }

    public set gameMode(v: string) {
        this._gameMode = v;
    }

    public get isOnline(): boolean {
        return this.gameMode == GameMode.ONLINE;
    }

    protected init() {

    }

    public reset() {
        this._gameMode = GameMode.SOLO;
        this._data = null;
    }

    async backToLobby() {
        if (this.isOnline) {
            let ret = await this.rpc_LeaveTable();
            if (ret.isSucc) {
                WorldMgr.reset();
                tgxSceneUtil.loadScene(SubGameMgr.current.entry);
            }
            else {
                tgxUIAlert.show(ret.err.message);
            }
        }
        else {
            tgxSceneUtil.loadScene(SubGameMgr.current.entry);
        }
    }

    backToLogin() {
        tgxUIAlert.show('网络链接断开，请重新登录').onClick(() => {
            this.reset();
            tgxSceneUtil.loadScene(SceneDef.LOGIN);
        });
    }

    public get worldId(): string {
        if (!this._data) {
            return '';
        }
        return this._data.id;
    }

    public get isPlayer(): boolean {
        if (!this._data) {
            return false;
        }

        let user = this.getUser(UserMgr.inst.userId);
        return user ? user.isPlayer : false;
    }

    public get isPlaying(): boolean {
        if (!this._data) {
            return false;
        }

        return this._data.isPlaying;
    }

    public get isWatcher(): boolean {
        if (!this._data) {
            return false;
        }

        let user = this.getUser(UserMgr.inst.userId);
        return user ? !user.isPlayer : false;
    }

    public get selfUser() {
        return this.getUser(UserMgr.inst.userId);
    }

    public getUser(uid: string) {
        for (let i = 0; i < this._data.userList.length; ++i) {
            let u = this._data.userList[i];
            if (u.uid == uid) {
                return u;
            }
        }
    }

    async enterWorld(params: SubWorldSceneParams) {
        SubGameMgr.gameMgr = this;
        this.reset();

        await WorldMgr.doEnterWorld(params);
        let conn = WorldMgr.worldConn;
        this.initNetMsgHandlers(conn);

        let ret = await WorldMgr.ensureConnected();
        if (ret.isSucc) {
            this.gameMode = GameMode.ONLINE;
            let conf = WorldMgr.subWorldConfig;
            await tgxSceneUtil.loadScene({ name: conf.scene, bundle: conf.bundle });
        }

        return ret;
    }

    initNetMsgHandlers(conn: WsClient<ServiceType>) {
        conn.flows.postRecvMsgFlow.push((v): any => {
        });
        conn.flows.postDisconnectFlow.push(v => {
            if (v.reason == 'normal' || v.reason == 'USER_LEAVE') {
                return;
            }
            if (!v.isManual) {
                this.backToLogin();
            }
            return v;
        });
        conn.listenMsg("SubWorldDismissedPush", this.onNet_subworld_dismissed_push.bind(this));

        conn.listenMsg("SubWorldDataSyncPush", this.onNet_subworld_data_sync_push.bind(this));
        conn.listenMsg("SubWorldDataChangedPush", this.onNet_subworld_data_changed_push.bind(this));
        conn.listenMsg("UserComesToTablePush", this.onNet_user_comes_to_table_push.bind(this));
        conn.listenMsg("UserDataChangedPush", this.onNet_user_data_changed_push.bind(this));
        conn.listenMsg("UserLeavesFromTablePush", this.onNet_user_leaves_from_table_push.bind(this));
    }

    async rpc_LeaveTable() {
        let ret = await WorldMgr.worldConn.callApi("ExitSubWorld", {});
        if (ret.isSucc) {
            //notice master service to remove flag.
            NetUtil.callMasterApi("LeaveSubWorld", { token: UserMgr.inst.token });
        }
        return ret;
    }

    async rpc_JoinGame() {
        let ret = await WorldMgr.worldConn.callApi("JoinGame", {});
        return ret;
    }

    async rpc_Ready() {
        let ret = await WorldMgr.worldConn.callApi("Ready", {});
        return ret;
    }

    //==========================
    onNet_subworld_dismissed_push(msg: MsgSubWorldDismissedPush) {

    }

    onNet_subworld_data_sync_push(msg: MsgSubWorldDataSyncPush) {
        this._data = msg.data;
    }
    onNet_subworld_data_changed_push(msg: MsgSubWorldDataChangedPush) {
        if (!this._data) {
            return;
        }

        if (msg.isPlaying !== undefined) {
            this._data.isPlaying = msg.isPlaying;
        }

        if(msg.numPlayer !== undefined){
            this._data.playerNum = msg.numPlayer;
        }

        /*
        if (msg.watcherList !== undefined) {
            this._gameData.watcherList = msg.watcherList;
            director.getScene().emit(SubWorldEvent.WATCHER_LIST_CHANGED);
        }
        */
    }

    onNet_user_comes_to_table_push(msg: MsgUserComesToTablePush) {
        if (!this._data) {
            return;
        }

        let user = this.getUser(msg.uid);
        if (!user) {
            this._data.userList.push(msg);
        }

        director.getScene().emit(SubWorldEvent.NEW_USER_COMES, msg);
    }

    onNet_user_data_changed_push(msg: MsgUserDataChangedPush) {
        if (!this._data) {
            return;
        }
        let userId = msg.uid;
        let u = this.getUser(userId);
        if (!u) {
            return;
        }
        if (msg.ready !== undefined) {
            u.ready = msg.ready;
        }

        if (msg.isPlayer !== undefined) {
            u.isPlayer = msg.isPlayer;
        }

        if(msg.isOnline !== undefined){
            u.isOnline = msg.isOnline;
        }

        director.getScene().emit(SubWorldEvent.USER_DATA_CHANGED, msg);
    }

    onNet_user_leaves_from_table_push(msg: MsgUserLeavesFromTablePush) {
        if (!this._data) {
            return;
        }

        let isPlayer = false;
        for (let i = 0; i < this._data.userList.length; ++i) {
            let p = this._data.userList[i];
            if (p.uid == msg.uid) {
                this._data.userList.splice(i, 1);
                isPlayer = p.isPlayer;
                break;
            }
        }

        director.getScene().emit(SubWorldEvent.USER_LEAVES, { userId: msg.uid, isPlayer: isPlayer });
    }
}